import os,re,datetime,cherrypy,StringIO
from sqlalchemy import *
from sqlalchemy.orm import *
from elixir import *
from elixir.options import options_defaults
from datetime import datetime, timedelta
from util import *
from rss import *

basepath = os.path.dirname(__file__) or os.getcwd()
con = ''
for i in open(os.path.join(basepath, '..', 'dev.cfg')):
    if i.startswith('sqlalchemy.dburi='):
        con = i[i.find('"')+1:-2]
        break
metadata.bind = create_engine(con,pool_recycle=3600)

uploadpath = os.path.join(basepath, 'static', 'file')

def flush():
    try:
        session.flush()
    except:
        session.rollback()
        
options_defaults['tablename'] = lambda cls:re.sub('([A-Za-z])([A-Z][a-z])','\\1_\\2',cls.__name__).lower()

class Viewable(Entity):
    using_options(inheritance='multi')
    
    title = Field(String(1000))
    parent = ManyToOne('Viewable', inverse='children')
    children = OneToMany('Viewable', inverse='parent')
    author = ManyToOne('User', inverse='viewables')
    permissions = ManyToMany('Permission', inverse='viewables')
    created = Field(DateTime, default=datetime.today())
    modified = Field(DateTime, default=datetime.today())

    def selfDestroy(self):
        for c in self.children:
            c.selfDestroy()
        self.delete()
        flush()

    def isA(self, cls):
        return isinstance(self, cls)

    @property
    def rssitem(self):
        return ''

    @property
    def url(self):
        return '/view?id=%d'%self.id

    @classmethod
    def destroy(cls, id=None):
        if id:
            v = cls.get(int(id))
            v.selfDestroy()

class Page(Viewable):
    using_options(inheritance='multi')
    
    url = Field(String(100), unique=True)
    summary = Field(String(2000))
    content = Field(Text)

    @property
    def rssitem(self):
        return createRssItem(self.title, self.url, self.desc, self.created)    

    @classmethod
    def find(cls, url=""):
        return cls.query.filter(cls.url == url).first()

    @classmethod
    def getURLs(cls):
        return [p.url for p in cls.query.filter(None)]

    @classmethod
    def addOrEdit(cls, title="", url="", summary="", content="", cid=None, id=None):
        if cid:
            collection = Collection.get(int(cid))
        else:
            collection = None
        if id:
            page = cls.get(int(id))
            page.title = title
            page.summary = summary
            page.content = content
            if url:
                page.url = url
            else:
                page.url = Util.toURL(title)
        else:
            page = cls(title=title, url=url, summary=summary, content=content)
        if collection:
            collection.children.append(page)
            page.parent = collection
        flush()
        return page
    
class Collection(Viewable):
    using_options(inheritance='multi')

    def createChildPage(self, title='', summary='', content=''):
        page = Page.addOrEdit(title, summary, content)
        page.parent = self
        self.children.append(page)

    def addChildPage(self, page):
        self.children.append(page)
        flush()

    def getPages(self):
        return Page.query.filter(parent=self)

    @classmethod    
    def addOrEdit(cls, title="", id=None):
        if id:
            c = cls.get(int(id))
            if cls.query.filter(cls.title == title).first().id != c.id:
                return c
            c.title = title
            c.modified = datetime.today()
        else:
            t = cls.query.filter(cls.title == title)
            if t.count() != 0:
                return t.first()
            else:
                c = cls(title = title)
        flush()
        return c
    
    def rss(self):
        return createRss('%s | Yale Club Beijing' % self.title,
                         self.url,
                         self.title,
                         [c.rssitem for c in self.children])    

class Gallery(Viewable):
    using_options(inheritance='multi')
    
    cover = ManyToOne('Photo')
    desc = Field(Text)

    def addPhoto(self, photo, cover=False):
        if not (photo in self.children):
            self.children.append(photo)
        if cover:
            self.cover = photo
        flush()

    def removePhoto(self, photo):
        if photo in self.children:
            self.children.remove(photo)
        if photo.parent == self:
            photo.selfDestroy()
        flush()

    def uploadPhoto(self, file, title="", desc="", cover=False):
        self.addPhoto(Photo.create(file, self, title, desc), cover)

    def rss(self):
        return createRss('%s | Yale Club Beijing' % self.title,
                         self.url,
                         self.desc,
                         [c.rssitem for c in self.children])

    @classmethod
    def addOrEdit(cls, title="", desc="", cover=None, id=None):
        if id:
            g = cls.get(int(id))
            g.title = title
            g.desc = desc
            g.cover = cover
            g.modified = datetime.today()
        else:
            g = cls(title=title, desc=desc, cover=cover)
        flush()
        return g

class File(Viewable):
    using_options(inheritance='multi')

    filename = Field(String(100), default=Util.genkey(), unique=True)
    desc = Field(Text)

    @property
    def rssitem(self):
        return createRssItem(self.title, self.url, self.desc, self.created)    

    def selfDestroy(self):
        for c in self.children:
            c.selfDestroy()
        try:
            os.remove(self.abspath)
        except:
            pass
        self.delete()
        flush()

    @property
    def src(self):
        return '/static/file/'+self.filename

    @property
    def abspath(self):
        return os.path.join(uploadpath, self.filename)

    @classmethod
    def create(cls, file, parent=None, title="", desc=""):
        filename = Util.genkey()+'.'+Util.getext(file.filename)
        targetfile = os.path.join(uploadpath, filename)
        f = open(targetfile, 'wb')
        f.write(file.file.read())
        f.close()
        im = cls(title=title, parent=parent, filename=filename, desc=desc)
        if parent:
            parent.children.append(im)
        flush()
        return im    
            
class Photo(File):
    using_options(inheritance='multi')

    @classmethod
    def create(cls, file, parent=None, title="", desc=""):
        if file.type[:5] == 'image':
            filename = Util.genkey()+'.'+Util.getext(file.filename)
            targetfile = os.path.join(uploadpath, filename)
            f = open(targetfile, 'wb')
            f.write(file.file.read())
            f.close()
            im = cls(title=title, parent=parent, filename=filename, desc=desc)
            if parent:
                parent.children.append(im)
            flush()
            return im

class Event(Viewable):
    using_options(inheritance='multi')
    
    summary = Field(String(2000))
    desc = Field(Text)
    location = Field(String(1000))
    starttime = Field(DateTime)
    endtime = Field(DateTime)
    rsvp = ManyToMany('User', inverse='events')

    def addRSVP(self, user=None):
        if user and not self.expired:
            self.rsvp.append(user)
            flush()
            
    @property
    def rssitem(self):
        return createRssItem(self.title, self.url, self.desc, self.created)

    @property
    def expired(self):
        return self.endtime < datetime.today()

    @property
    def time(self):
        if self.starttime.date() == self.endtime.date():
            return "%s - %s" % (self.starttime.strftime("%m/%d/%Y %I:%M%p"), self.endtime.strftime("%I:%M%p"))
        else:
            return "%s - %s" % (self.starttime.strftime("%m/%d/%Y %I:%M%p"), self.endtime.strftime("%m/%d/%Y %I:%M%p"))

    @classmethod
    def getAllEvents(cls):
        return cls.query.filter(None)

    @classmethod
    def getPastEvents(cls):
        return cls.query.filter(cls.endtime < datetime.today())

    @classmethod
    def getActiveEvents(cls):
        return cls.query.filter(cls.endtime >= datetime.today())

    @classmethod
    def getEvents(cls, all=False, date=''):
        if date:
            d1 = datetime.strptime(date, '%Y%m%d')
            d2 = d1 + timedelta(days=1)
            return cls.query.filter(Event.starttime >= d1).filter(Event.starttime <= d2)
        else:
            if all:
                return cls.getAllEvents()
            else:
                return cls.getActiveEvents()

    @classmethod
    def addOrEdit(cls, title="", summary="", desc="", location="", starttime="", endtime="", id=None):
        if id:
            event = cls.get(int(id))
            event.title = title
            event.summary = summary
            event.desc = desc
            event.location = location
            event.starttime = Util.toDateTime(starttime)
            event.endtime = Util.toDateTime(endtime)
            event.modified = datetime.today()
        else:
            event = cls(title=title, summary=summary, desc=desc, location=location, starttime=Util.toDateTime(starttime), endtime=Util.toDateTime(endtime))
        flush()
        return event

    @classmethod
    def rss(cls):
        return createRss('Events | Yale Club Beijing',
                         'http://www.yaleclubbeijing.com/events',
                         'Upcoming events of Yale Club Beijing',
                         [evt.rssitem for evt in cls.query.filter(None)])

class News(Viewable):
    using_options(inheritance='multi')

    summary = Field(String(2000))
    content = Field(Text)
    
    @property
    def rssitem(self):
        return createRssItem(self.title, self.url, self.desc, self.created)

    @classmethod
    def addOrEdit(cls, title="", summary="", content="", id=None):
        if id:
            news = cls.get(int(id))
            news.title = title
            news.summary = summary
            news.content = content
            news.modified = datetime.today()
        else:
            news = cls(title=title, summary=summary, content=content)
        flush()
        return news

    @classmethod
    def rss(cls):
        return createRss('News | Yale Club Beijing',
                         'http://www.yaleclubbeijing.com/news',
                         'News of Yale Club Beijing',
                         [news.rssitem for news in cls.query.filter(None)])     
    
class Permission(Entity):
    VIEW = 1
    EDIT = 2
    permission = Field(Integer, default = 0)
    viewables = ManyToMany('Viewable', inverse='permissions')
    group = ManyToOne('Group', inverse='permissions')

class Group(Entity):
    name = Field(String(200), unique=True)
    users = ManyToMany('User', inverse='groups')
    permissions = OneToMany('Permission', inverse='group')

class User(Entity):
    groups = ManyToMany('Group', inverse='users')
    name = Field(String(200))
    email = Field(String(200), unique=True)
    phone = Field(String(200))
    dob = Field(DateTime)
    desc = Field(Text)
    im = OneToMany('IM', inverse='user')
    website = Field(String(200))
    viewables = OneToMany('Viewable', inverse='author')
    payments = OneToMany('Payment', inverse='user')
    events = ManyToMany('Event', inverse='rsvp')

    def attend(self, event=None):
        if not event.expired:
            event.addRSVP(self)

    @property
    def dobstr(self):
        return Util.toDateTimeStr(self.dob)

    @classmethod
    def addOrEdit(cls, name, email, phone="", dob="", desc="", website="", id=None):
        dob = Util.toDateTime(dob)
        if id:
            user = cls.get(int(id))
            user.name = name
            user.email = email
            user.phone = phone
            user.dob = dob
            user.desc = desc
            user.website = website
        else:
            user = cls(name=name, email=email, phone=phone, dob=dob, desc=desc, website=website)
        flush()
        return user

    @classmethod
    def exportUsers(cls, filename=None):
        csv = []
        for u in cls.query.filter(None):
            infos = [u.name, u.email, u.phone, u.dobstr, u.desc, u.website]
            csv.append(','.join(map(Util.quote, infos)))
        filename = 'users.csv'
        f = open(os.path.join(uploadpath, filename), 'w')
        f.write('\n'.join(csv))
        f.close()
        f = File(filename = filename)
        flush()
        return f

    @classmethod
    def importUsers(self, filename):
        pass
        
    
class Payment(Entity):
    user = ManyToOne('User', inverse='payments')
    amount = Field(Float)
    desc = Field(String(1000))
    time = Field(DateTime, default = datetime.today())
    
class IM(Entity):
    TYPE_MSN = 1
    TYPE_GTALK = 2
    TYPE_QQ = 3
    TYPE_SKYPE = 4
    TYPE_AIM = 5
    user = ManyToOne('User', inverse='im')
    type = Field(Integer)
    account = Field(String(200))

def reset_all():
    drop_all()
    create_all()
    initDatabase()
    
def initDatabase():
    try:
        anony = Group(name="Anonymous Users")
        logged = Group(name="Logged in Users")
        admin = Group(name="Administrators")
        paid = Group(name="Donors")
        session.commit()
    except:
        pass
    
setup_all()

